1 module hip.windowing.platforms.x11; 2 3 version(X11): 4 5 import core.stdc.stdio; 6 7 import hip.windowing.platforms.x11lib.glx; 8 import hip.windowing.platforms.x11lib.x11; 9 import hip.windowing.events; 10 import hip.windowing.input; 11 12 public import hip.windowing.platforms.x11lib.x11; 13 14 package struct X11WindowData 15 { 16 Display* display; 17 Window window; 18 Screen* screen; 19 XVisualInfo* visual; 20 int screenId; 21 int width, height; 22 Colormap colormap; 23 GLXContext glContext; 24 Atom atomWmDeleteWindow; 25 26 } 27 package X11WindowData x11win; 28 29 package __gshared bool ctxErrorOccurred = false; 30 31 package extern(C) nothrow @system @nogc 32 int ctxErrorHandler( Display *dpy, XErrorEvent *ev ) 33 { 34 ctxErrorOccurred = true; 35 return 0; 36 } 37 38 39 nothrow @nogc bool initializeOpenGL(int majorVersion, int minorVersion, void* WindowHandle) 40 { 41 GLint[23] glxAttribs = [ 42 GLX_X_RENDERABLE , True, 43 GLX_DRAWABLE_TYPE , GLX_WINDOW_BIT, 44 GLX_RENDER_TYPE , GLX_RGBA_BIT, 45 GLX_X_VISUAL_TYPE , GLX_TRUE_COLOR, 46 GLX_RED_SIZE , 8, 47 GLX_GREEN_SIZE , 8, 48 GLX_BLUE_SIZE , 8, 49 GLX_ALPHA_SIZE , 8, 50 GLX_DEPTH_SIZE , 24, 51 GLX_STENCIL_SIZE , 8, 52 GLX_DOUBLEBUFFER , True, 53 None 54 ]; 55 56 int fbcount; 57 GLXFBConfig* fbc = glXChooseFBConfig(x11win.display, x11win.screenId, glxAttribs.ptr, &fbcount); 58 if (fbc is null || fbcount == 0) 59 { 60 printf("Failed to retrieve framebuffer.\n"); 61 XCloseDisplay(x11win.display); 62 return 1; 63 } 64 65 // Pick the FB config/visual with the most samples per pixel 66 int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999; 67 for (int i = 0; i < fbcount; ++i) { 68 XVisualInfo *vi = glXGetVisualFromFBConfig( x11win.display, fbc[i] ); 69 if ( vi != null) { 70 int samp_buf, samples; 71 glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf ); 72 glXGetFBConfigAttrib( x11win.display, fbc[i], GLX_SAMPLES , &samples ); 73 74 if ( best_fbc < 0 || (samp_buf && samples > best_num_samp) ) { 75 best_fbc = i; 76 best_num_samp = samples; 77 } 78 if ( worst_fbc < 0 || !samp_buf || samples < worst_num_samp ) 79 worst_fbc = i; 80 worst_num_samp = samples; 81 } 82 XFree( vi ); 83 } 84 85 86 //Some would try to get the best framebuffer, but, is that really necessary? Get the first 87 GLXFBConfig bestFbc = fbc[best_fbc]; 88 XFree(fbc); 89 x11win.visual = glXGetVisualFromFBConfig(x11win.display, bestFbc); 90 if(x11win.visual == null) 91 { 92 printf("Could not create correct visual window \n"); 93 XCloseDisplay(x11win.display); 94 return false; 95 } 96 97 //Open the window 98 XSetWindowAttributes windowAttribs; 99 windowAttribs.border_pixel = BlackPixel(x11win.display, x11win.screenId); 100 windowAttribs.background_pixel = WhitePixel(x11win.display, x11win.screenId); 101 windowAttribs.override_redirect = True; 102 windowAttribs.colormap = XCreateColormap( 103 x11win.display, RootWindow(x11win.display, x11win.screenId), x11win.visual.visual, AllocNone 104 ); 105 106 windowAttribs.event_mask = PointerMotionMask | 107 ButtonPressMask | 108 ButtonReleaseMask | 109 KeyPressMask | 110 KeyReleaseMask | 111 EnterWindowMask | 112 LeaveWindowMask | 113 ExposureMask; 114 115 116 117 x11win.window = XCreateWindow( 118 x11win.display, 119 RootWindow(x11win.display, x11win.screenId), 120 0, 0, x11win.width, x11win.height, 121 0, x11win.visual.depth, InputOutput, x11win.visual.visual, 122 CWBackPixel | CWColormap | CWBorderPixel | CWEventMask, 123 &windowAttribs 124 ); 125 if(!x11win.window) 126 { 127 printf("Could not create XWindow\n"); 128 return false; 129 } 130 131 Atom atomWmDeleteWindow = XInternAtom(x11win.display, "WM_DELETE_WINDOW", False); 132 if(atomWmDeleteWindow == BadAlloc) 133 { 134 printf("X Server failed to allocate WM_DELETE_WINDOW\n"); 135 return false; 136 } 137 else if(atomWmDeleteWindow == BadValue) 138 { 139 printf("WM_DELETE_WINDOW is not a valid argument\n"); 140 return false; 141 } 142 x11win.atomWmDeleteWindow = atomWmDeleteWindow; 143 Status st = XSetWMProtocols(x11win.display, x11win.window, &atomWmDeleteWindow, 1); 144 if(st == BadAlloc) 145 { 146 printf("XServer failed to allocate resources for SetWMProtocols\n"); 147 return false; 148 } 149 else if(st == BadWindow) 150 { 151 printf("The window argument does not name a defined window\n"); 152 return false; 153 } 154 155 const(char)* glExts = glXQueryExtensionsString(x11win.display, DefaultScreen(x11win.display)); 156 157 glXCreateContextAttribsARBProc glXCreateContextAttribsARB; 158 glXCreateContextAttribsARB = cast(glXCreateContextAttribsARBProc)glXGetProcAddressARB(cast(const(GLubyte)*)"glXCreateContextAttribsARB"); 159 160 GLXContext glContext; 161 162 163 if(!isExtensionSupported(glExts, "GLX_ARB_create_context") || glXCreateContextAttribsARB is null) 164 { 165 printf("glXCreateContextAttribsARB() not found, using old style GLX context"); 166 x11win.glContext = glContext = glXCreateNewContext(x11win.display, bestFbc, GLX_RGBA_TYPE, null, True); 167 } 168 else 169 { 170 int[7] context_attribs = 171 [ 172 GLX_CONTEXT_MAJOR_VERSION_ARB, 3, 173 GLX_CONTEXT_MINOR_VERSION_ARB, 3, //3.3 is the minimum here 174 GLX_CONTEXT_FLAGS_ARB , GLX_CONTEXT_CORE_PROFILE_BIT_ARB, 175 None 176 ]; 177 178 x11win.glContext = glContext = glXCreateContextAttribsARB(x11win.display, bestFbc, null, 179 True, context_attribs.ptr ); 180 } 181 if(x11win.glContext == null) 182 { 183 printf("Could not create GLX Context\n"); 184 return false; 185 } 186 XSync(x11win.display, False); 187 188 if (!glXIsDirect (x11win.display, x11win.glContext)) 189 printf("Indirect GLX rendering context obtained\n"); 190 if(glXMakeCurrent(x11win.display, x11win.window, x11win.glContext) == 0) 191 { 192 printf("Could not make GLX Context as current\n"); 193 return false; 194 } 195 //Show Window 196 XClearWindow(x11win.display, x11win.window); 197 XMapRaised(x11win.display, x11win.window); 198 XStoreName(x11win.display, x11win.window, "HipremeEngine"); 199 200 string[] errors; 201 setVsyncActive(false, null, errors); 202 203 204 return true; 205 } 206 207 void show(void* WindowHandle){} 208 void swapBuffer() 209 { 210 glXSwapBuffers(x11win.display, x11win.window); 211 } 212 213 void setVsyncActive(bool active, void* WindowHandle, ref string[] errors) @nogc nothrow @system 214 { 215 static bool loadedSymbols = false; 216 if(!loadedSymbols) 217 { 218 glXSwapIntervalEXT = cast(glXSwapIntervalEXTProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalEXT"); 219 glXSwapIntervalMESA = cast(glXSwapIntervalMESAProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalMESA"); 220 glXSwapIntervalSGI = cast(glXSwapIntervalSGIProc)glXGetProcAddressARB(cast(GLubyte*)"glXSwapIntervalSGI"); 221 loadedSymbols = true; 222 } 223 glXSwapIntervalEXT(x11win.display, x11win.window, cast(int)active); 224 glXSwapIntervalMESA(cast(int)active); 225 glXSwapIntervalSGI(cast(int)active); 226 } 227 228 void setWindowName(string name, void* WindowHandle, ref string[] errors) 229 { 230 XStoreName(x11win.display, x11win.window, name.ptr); 231 } 232 233 234 pragma(inline) wchar convertKeycodeToScancode(XKeyEvent* ev) 235 { 236 char[2] buffer = '\0'; 237 KeySym ks; 238 int allocated = XLookupString(ev, buffer.ptr, buffer.length, &ks, null); 239 if(allocated > 1) 240 printf("%*s", cast(int)buffer.length, buffer.ptr); 241 return *cast(wchar*)(cast(void*)buffer.ptr); 242 } 243 244 void poll() 245 { 246 XEvent ev; 247 while (XPending(x11win.display) > 0) 248 { 249 XNextEvent(x11win.display, &ev); 250 switch(ev.type) 251 { 252 case Expose: 253 { 254 XWindowAttributes attribs; 255 XGetWindowAttributes(x11win.display, x11win.window, &attribs); 256 printf("Expose event\n"); 257 break; 258 } 259 case ClientMessage: 260 if(ev.xclient.data.l[0] == x11win.atomWmDeleteWindow && onWindowClosed != null) 261 onWindowClosed(); 262 break; 263 case DestroyNotify: 264 { 265 if(onWindowClosed != null) 266 onWindowClosed(); 267 break; 268 } 269 case ButtonPress: 270 { 271 int x = ev.xbutton.x; 272 int y = ev.xbutton.y; 273 switch(ev.xbutton.button) 274 { 275 case 1: //Left 276 if(onMouseDown != null) 277 onMouseDown(HipWindowingMouseButton.left, x, y); 278 break; 279 case 2: //Middle 280 if(onMouseDown != null) 281 onMouseDown(HipWindowingMouseButton.middle, x, y); 282 break; 283 case 3: //Right 284 if(onMouseDown != null) 285 onMouseDown(HipWindowingMouseButton.right, x, y); 286 break; 287 case 4: //Scroll up 288 if(onMouseWheel != null) 289 onMouseWheel(0, -1); 290 break; 291 case 5: //Scroll down 292 if(onMouseWheel != null) 293 onMouseWheel(0, 1); 294 break; 295 default: break; 296 } 297 } break; 298 case ButtonRelease: 299 { 300 int x = ev.xbutton.x; 301 int y = ev.xbutton.y; 302 switch(ev.xbutton.button) 303 { 304 case 1: //Left 305 if(onMouseUp != null) 306 onMouseUp(HipWindowingMouseButton.left, x, y); 307 break; 308 case 2: //Middle 309 if(onMouseUp != null) 310 onMouseUp(HipWindowingMouseButton.middle, x, y); 311 break; 312 case 3: //Right 313 if(onMouseUp != null) 314 onMouseUp(HipWindowingMouseButton.right, x, y); 315 break; 316 default: break; 317 } 318 } break; 319 case MotionNotify: 320 if(onMouseMove != null) 321 onMouseMove(ev.xmotion.x, ev.xmotion.y); 322 break; 323 case KeyPress: 324 if(onKeyDown != null) 325 onKeyDown(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0)); 326 // onKeyDown(convertKeycodeToScancode(&ev.xkey)); 327 break; 328 case KeyRelease: 329 if(onKeyUp != null) 330 onKeyUp(cast(uint)XKeycodeToKeysym(x11win.display, ev.xkey.keycode, ev.xkey.state & ShiftMask ? 1 : 0)); 331 // onKeyUp(convertKeycodeToScancode(&ev.xkey)); 332 break; 333 default:break; 334 } 335 } 336 } 337 338 ///Returns [width, height] 339 int[2] getWindowSize(void* WindowHandle, ref string[] errors) @nogc 340 { 341 XWindowAttributes att; 342 XGetWindowAttributes(x11win.display, x11win.window, &att); 343 return [att.width, att.height]; 344 } 345 346 void setWindowSize(int width, int height, void* WindowHandle, ref string[] errors) @nogc 347 { 348 uint change_values = CWWidth | CWHeight; 349 XWindowChanges values; 350 values.width = width; 351 values.height = height; 352 XConfigureWindow(x11win.display, x11win.window, change_values, &values); 353 } 354 import hip.windowing.platforms.null_; 355 alias setFullscreen = hip.windowing.platforms.null_.setFullscreen; 356 357 bool destroy_GL_Context() @nogc 358 { 359 XDestroyWindow(x11win.display, x11win.window); 360 XCloseDisplay(x11win.display); 361 XFree(x11win.visual); 362 XFreeColormap(x11win.display, x11win.colormap); 363 glXDestroyContext(x11win.display, x11win.glContext); 364 return true; 365 } 366 367 int openWindow(int width, int height, out void* WindowHandle) @nogc 368 { 369 x11win.width = width; 370 x11win.height = height; 371 //Open the display 372 x11win.display = XOpenDisplay(null); 373 if(x11win.display == null) 374 { 375 printf("Could not open display.\n"); 376 return 1; 377 } 378 int glx_major, glx_minor; 379 if ( !glXQueryVersion(x11win.display, &glx_major, &glx_minor ) || 380 ( ( glx_major == 1 ) && ( glx_minor < 3 ) ) || ( glx_major < 1 ) ) 381 { 382 printf("Invalid GLX version\n"); 383 return 1; 384 } 385 x11win.screenId = DefaultScreen(x11win.display); 386 387 return 0; 388 }